/***************************************************************************
 * Bytecode Viewer (BCV) - Java & Android Reverse Engineering Suite        *
 * Copyright (C) 2014 Konloch - Konloch.com / BytecodeViewer.com           *
 *                                                                         *
 * This program is free software: you can redistribute it and/or modify    *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation, either version 3 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program.  If not, see <http://www.gnu.org/licenses/>. *
 ***************************************************************************/

package the.bytecode.club.bytecodeviewer.gui.hexviewer;

import org.exbin.bined.CodeAreaUtils;

import javax.annotation.ParametersAreNonnullByDefault;

/**
 * Go-to position panel for binary editor.
 */
@ParametersAreNonnullByDefault
public class GoToBinaryPanel extends javax.swing.JPanel
{

    private long cursorPosition;
    private long maxPosition;
    private GoToBinaryPositionMode goToMode = GoToBinaryPositionMode.FROM_START;

    public GoToBinaryPanel()
    {
        initComponents();

        baseSwitchableSpinnerPanel.setMinimum(0L);
        baseSwitchableSpinnerPanel.addChangeListener((javax.swing.event.ChangeEvent evt) -> updateTargetPosition());
    }

    /**
     * This method is called from within the constructor to initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is always
     * regenerated by the Form Editor.
     */
    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
    private void initComponents()
    {

        positionTypeButtonGroup = new javax.swing.ButtonGroup();
        currentPositionLabel = new javax.swing.JLabel();
        currentPositionTextField = new javax.swing.JTextField();
        goToPanel = new javax.swing.JPanel();
        fromStartRadioButton = new javax.swing.JRadioButton();
        fromEndRadioButton = new javax.swing.JRadioButton();
        fromCursorRadioButton = new javax.swing.JRadioButton();
        positionLabel = new javax.swing.JLabel();
        baseSwitchableSpinnerPanel = new BaseSwitchableSpinnerPanel();
        targetPositionLabel = new javax.swing.JLabel();
        targetPositionTextField = new javax.swing.JTextField();

        currentPositionLabel.setText("Current Position");

        currentPositionTextField.setEditable(false);
        currentPositionTextField.setText("0");

        goToPanel.setBorder(javax.swing.BorderFactory.createTitledBorder("Go To Position"));

        positionTypeButtonGroup.add(fromStartRadioButton);
        fromStartRadioButton.setSelected(true);
        fromStartRadioButton.setText("Position from start");
        fromStartRadioButton.addItemListener(this::fromStartRadioButtonItemStateChanged);

        positionTypeButtonGroup.add(fromEndRadioButton);
        fromEndRadioButton.setText("Position from end");
        fromEndRadioButton.addItemListener(this::fromEndRadioButtonItemStateChanged);

        positionTypeButtonGroup.add(fromCursorRadioButton);
        fromCursorRadioButton.setText("Position relative to cursor");
        fromCursorRadioButton.addItemListener(this::fromCursorRadioButtonItemStateChanged);

        positionLabel.setText("Position");

        javax.swing.GroupLayout goToPanelLayout = new javax.swing.GroupLayout(goToPanel);
        goToPanel.setLayout(goToPanelLayout);
        goToPanelLayout.setHorizontalGroup(goToPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING).addComponent(fromStartRadioButton, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE).addComponent(fromCursorRadioButton, javax.swing.GroupLayout.DEFAULT_SIZE, 412, Short.MAX_VALUE).addComponent(fromEndRadioButton, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE).addGroup(goToPanelLayout.createSequentialGroup().addContainerGap().addGroup(goToPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING).addComponent(baseSwitchableSpinnerPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE).addGroup(goToPanelLayout.createSequentialGroup().addComponent(positionLabel).addGap(0, 0, Short.MAX_VALUE))).addContainerGap()));
        goToPanelLayout.setVerticalGroup(goToPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING).addGroup(goToPanelLayout.createSequentialGroup().addComponent(fromStartRadioButton, javax.swing.GroupLayout.PREFERRED_SIZE, 22, javax.swing.GroupLayout.PREFERRED_SIZE).addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED).addComponent(fromEndRadioButton, javax.swing.GroupLayout.PREFERRED_SIZE, 22, javax.swing.GroupLayout.PREFERRED_SIZE).addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED).addComponent(fromCursorRadioButton, javax.swing.GroupLayout.PREFERRED_SIZE, 20, javax.swing.GroupLayout.PREFERRED_SIZE).addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED).addComponent(positionLabel).addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED).addComponent(baseSwitchableSpinnerPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE).addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)));

        targetPositionLabel.setText("Target Position");

        targetPositionTextField.setEditable(false);
        targetPositionTextField.setText("0");

        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
        this.setLayout(layout);
        layout.setHorizontalGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING).addGroup(layout.createSequentialGroup().addContainerGap().addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING).addComponent(currentPositionTextField).addComponent(goToPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE).addComponent(targetPositionTextField).addGroup(layout.createSequentialGroup().addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING).addComponent(currentPositionLabel).addComponent(targetPositionLabel)).addGap(0, 0, Short.MAX_VALUE))).addContainerGap()));
        layout.setVerticalGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING).addGroup(layout.createSequentialGroup().addContainerGap().addComponent(currentPositionLabel).addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED).addComponent(currentPositionTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE).addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED).addComponent(goToPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE).addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED).addComponent(targetPositionLabel).addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED).addComponent(targetPositionTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE).addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)));
    }// </editor-fold>//GEN-END:initComponents

    private void fromStartRadioButtonItemStateChanged(java.awt.event.ItemEvent evt)
    {//GEN-FIRST:event_fromStartRadioButtonItemStateChanged
        if (fromStartRadioButton.isSelected())
        {
            switchGoToMode(GoToBinaryPositionMode.FROM_START);
        }
    }//GEN-LAST:event_fromStartRadioButtonItemStateChanged

    private void fromEndRadioButtonItemStateChanged(java.awt.event.ItemEvent evt)
    {//GEN-FIRST:event_fromEndRadioButtonItemStateChanged
        if (fromEndRadioButton.isSelected())
        {
            switchGoToMode(GoToBinaryPositionMode.FROM_END);
        }
    }//GEN-LAST:event_fromEndRadioButtonItemStateChanged

    private void fromCursorRadioButtonItemStateChanged(java.awt.event.ItemEvent evt)
    {//GEN-FIRST:event_fromCursorRadioButtonItemStateChanged
        if (fromCursorRadioButton.isSelected())
        {
            switchGoToMode(GoToBinaryPositionMode.FROM_CURSOR);
        }
    }//GEN-LAST:event_fromCursorRadioButtonItemStateChanged

    private void updateTargetPosition()
    {
        targetPositionTextField.setText(String.valueOf(getTargetPosition()));
    }

    public void initFocus()
    {
        baseSwitchableSpinnerPanel.initFocus();
    }

    public long getTargetPosition()
    {
        long absolutePosition;
        long position = getPositionValue();
        switch (goToMode)
        {
            case FROM_START:
                absolutePosition = position;
                break;
            case FROM_END:
                absolutePosition = maxPosition - position;
                break;
            case FROM_CURSOR:
                absolutePosition = cursorPosition + position;
                break;
            default:
                throw CodeAreaUtils.getInvalidTypeException(goToMode);
        }

        if (absolutePosition < 0)
        {
            absolutePosition = 0;
        }
        else if (absolutePosition > maxPosition)
        {
            absolutePosition = maxPosition;
        }
        return absolutePosition;
    }

    public void setTargetPosition(long absolutePosition)
    {
        if (absolutePosition < 0)
        {
            absolutePosition = 0;
        }
        else if (absolutePosition > maxPosition)
        {
            absolutePosition = maxPosition;
        }
        switch (goToMode)
        {
            case FROM_START:
                setPositionValue(absolutePosition);
                break;
            case FROM_END:
                setPositionValue(maxPosition - absolutePosition);
                break;
            case FROM_CURSOR:
                setPositionValue(absolutePosition - cursorPosition);
                break;
            default:
                throw CodeAreaUtils.getInvalidTypeException(goToMode);
        }
        updateTargetPosition();
    }

    public long getCursorPosition()
    {
        return cursorPosition;
    }

    public void setCursorPosition(long cursorPosition)
    {
        this.cursorPosition = cursorPosition;
        setPositionValue(cursorPosition);
        currentPositionTextField.setText(String.valueOf(cursorPosition));
    }

    public void setMaxPosition(long maxPosition)
    {
        this.maxPosition = maxPosition;
        baseSwitchableSpinnerPanel.setMaximum(maxPosition);
        updateTargetPosition();
    }

    public void setSelected()
    {
        baseSwitchableSpinnerPanel.requestFocusInWindow();
    }

    private void switchGoToMode(GoToBinaryPositionMode goToMode)
    {
        if (this.goToMode == goToMode)
        {
            return;
        }

        long absolutePosition = getTargetPosition();
        this.goToMode = goToMode;
        switch (goToMode)
        {
            case FROM_START:
            case FROM_END:
            {
                setPositionValue(0L);
                baseSwitchableSpinnerPanel.setMinimum(0L);
                baseSwitchableSpinnerPanel.setMaximum(maxPosition);
                baseSwitchableSpinnerPanel.revalidateSpinner();
                break;
            }
            case FROM_CURSOR:
            {
                setPositionValue(0L);
                baseSwitchableSpinnerPanel.setMinimum(-cursorPosition);
                baseSwitchableSpinnerPanel.setMaximum(maxPosition - cursorPosition);
                baseSwitchableSpinnerPanel.revalidateSpinner();
                break;
            }
            default:
                throw CodeAreaUtils.getInvalidTypeException(goToMode);
        }
        setTargetPosition(absolutePosition);
    }

    private long getPositionValue()
    {
        return baseSwitchableSpinnerPanel.getValue();
    }

    private void setPositionValue(long value)
    {
        baseSwitchableSpinnerPanel.setValue(value);
        updateTargetPosition();
        //        positionSpinner.setValue(value);
        //        positionSpinner.firePropertyChange(SPINNER_PROPERTY, value, value);
    }

    public void acceptInput()
    {
        baseSwitchableSpinnerPanel.acceptInput();
    }

    // Variables declaration - do not modify//GEN-BEGIN:variables
    private BaseSwitchableSpinnerPanel baseSwitchableSpinnerPanel;
    private javax.swing.JLabel currentPositionLabel;
    private javax.swing.JTextField currentPositionTextField;
    private javax.swing.JRadioButton fromCursorRadioButton;
    private javax.swing.JRadioButton fromEndRadioButton;
    private javax.swing.JRadioButton fromStartRadioButton;
    private javax.swing.JPanel goToPanel;
    private javax.swing.JLabel positionLabel;
    private javax.swing.ButtonGroup positionTypeButtonGroup;
    private javax.swing.JLabel targetPositionLabel;
    private javax.swing.JTextField targetPositionTextField;
    // End of variables declaration//GEN-END:variables

}
